package gecko;

import gecko.lang.TypedMap;
import gecko.x.ClassX;
import gecko.x.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * @author 陈永佳 (yoojiachen@gmail.com)
 * @version 0.0.1
 */
class GenericEngine {

    private static final Logger LOGGER = LoggerFactory.getLogger(GenericEngine.class);

    private final static long OP_TIMEOUT_MS = 1000;

    protected final List<VirtualPlugin> mPlugins = new ArrayList<>();
    protected final Map<String, DevicePipeline> mPipelines = new HashMap<>();
    protected final List<Interceptor> mInterceptors = new ArrayList<>();
    protected final List<Driver> mDrivers = new ArrayList<>();
    protected final List<VirtualTrigger> mTriggers = new ArrayList<>();

    /**
     * 添加一个设备对象。
     * 设备对象的地址必须唯一。如果设备地址重复，会抛出异常。
     *
     * @param device 设备对象
     */
    public void addVirtualDevice(VirtualDevice device) {
        final String pn = device.getProtocolName();
        final DevicePipeline pipeline = mPipelines.get(pn);
        if (null != pipeline) {
            pipeline.addVirtualDevice(device);
        } else {
            throw new IllegalArgumentException("未找到对应协议的DevicePipeline，协议: " + pn + ", Device: " + ClassX.classNameOf(device));
        }
    }

    /**
     * 添加Plugin
     *
     * @param plugin Bundle
     */
    public void addPlugin(VirtualPlugin plugin) {
        mPlugins.add(plugin);
    }

    /**
     * 添加 Interceptor
     *
     * @param interceptor 拦截器
     */
    public void addInterceptor(Interceptor interceptor) {
        this.mInterceptors.add(interceptor);
    }

    /**
     * 添加 Driver
     *
     * @param driver Driver
     */
    public void addDriver(Driver driver) {
        this.mDrivers.add(driver);
    }

    /**
     * 添加 DevicePipeline
     *
     * @param pipeline DevicePipeline
     */
    public void addDevicePipeline(DevicePipeline pipeline) {
        final String rk = pipeline.getSupportProtocol();
        if (!mPipelines.containsKey(rk)) {
            mPipelines.put(rk, pipeline);
        } else {
            throw new IllegalArgumentException("已存在相同Key的DevicePipeline: " + rk);
        }
    }

    /**
     * 添加触发器
     *
     * @param trigger Trigger
     */
    public void addTrigger(VirtualTrigger trigger) {
        mTriggers.add(trigger);
    }

    ////

    // 动态加载模块
    static Stream<ConfigEntity> initDynamicBundles(Map<String, TypedMap> configs) {
        return configs.entrySet().stream()
                .map(e -> ConfigEntity.parse(e.getKey(), e.getValue()))
                .filter(c -> {
                    // className不是类全路径，是SPI配置
                    if (!c.className.contains(".")) {
                        return false;
                    }
                    // 禁用组件
                    if (c.disable) {
                        LOGGER.info("Bundle[{}] 被设置为DISABLE状态： {}", c.displayName, c.className);
                        return false;
                    } else {
                        return true;
                    }
                });
    }

    // SPI组件初始化
    static <T extends InitAware> void initSPIBundles(GeckoScoped scoped,
                                                     Collection<T> spiLoadedMods,
                                                     String action,
                                                     BiConsumer<T, ConfigEntity> confFunc,
                                                     Function<String, TypedMap> configFunc,
                                                     Consumer<Collection<T>> removeFunc) {
        final List<T> shouldRemove = new ArrayList<>();
        spiLoadedMods.parallelStream().forEach(it -> {
            // SPI组件的配置
            final String classShortName = ClassX.nameOf(it);
            // SPI组件初始化读取的配置信息，是以SPI实现类的类名简称（如：GPIODevice）为Key的配置。
            final ConfigEntity conf = ConfigEntity.parse(classShortName,
                    configFunc.apply(classShortName));
            if (conf.disable) {
                LOGGER.info("{} [{}] 被设置为DISABLE状态： {}", action, classShortName, conf.className);
                shouldRemove.add(it);
                return;
            }
            LOGGER.debug("初始化SPI组件： {}", classShortName);
            confFunc.accept(it, conf);
            scoped.detectTimeout(action, OP_TIMEOUT_MS, () -> it.onInit(conf.initArgs, scoped));
        });
        // remove disabled
        if (!shouldRemove.isEmpty()) {
            removeFunc.accept(shouldRemove);
        }
    }

    static TypedMap getSPIConf(Map<String, TypedMap> config, String key) {
        return config.getOrDefault("SPI", TypedMap.empty()).getDictMap(key);
    }

    static void checkAndSetHardware(VirtualHardware it, ConfigEntity c) {
        final String className = c.className;
        it.setGroupAddress(Strings.requireNotEmpty(c.groupAddress, "GroupAddress is empty: " + className));
        it.setPhysicalAddress(Strings.requireNotEmpty(c.physicalAddress, "PhysicalAddress is empty: " + className));
        it.setDisplayName(Strings.requireNotEmpty(c.displayName, "DisplayName is empty: " + className));
    }


    static <T> void logEachMods(GeckoScoped scoped, Collection<T> mods, String modType) {
        scoped.LogIfV(() ->
                mods.forEach(it ->
                        LOGGER.info("  {}: {}", modType, ClassX.classNameOf(it))));
    }
}
